/*************************************************************************
Crytek Source File.
Copyright (C), Crytek Studios, 2001-2010.
-------------------------------------------------------------------------
Description:
This file contains the Flow Nodes for POBIS (Procedural Object Interaction
system). The main code for this system can be found in the file
IKInteractionManager.

History:
- Mar 2nd, 2010  Created by Michelle Martin
*************************************************************************/
#include "StdAfx.h"
#include "FlowBaseNode.h"
#include "IKObjectInteractionSystem/IKInteractionManager.h"  // to start/stop and query interactions
#include "IKObjectInteractionSystem/ScriptBind_IKInteractions.h" // for the helper function ReadAditionalPropertiesFromEntity()


class CFlowNode_StartInteraction : public CFlowBaseNode<eNCT_Instanced>
{
	enum INPUTS
	{
		EIP_TargetEntity = 0,
		EIP_StartInteraction,
		EIP_StopInteraction,
		EIP_Limb1,
		EIP_Limb2,
		EIP_ObjectType,
		EIP_AllowRegrip,
		EIP_AdditionalOffsetPos,
		EIP_AdditionalOffsetRot,
	};

	enum OUTPUTS
	{
		EOP_Success,
		EOP_Failed,
		EOP_Finished,
	};

	int m_interactionID;

public:
	CFlowNode_StartInteraction( SActivationInfo * pActInfo ) 
	{ 
		m_interactionID = -1;
	}

	void GetConfiguration( SFlowNodeConfig& config )
	{
		static const SInputPortConfig in_ports[] = 
		{
			InputPortConfig<EntityId>("TargetEntity", _HELP("Entity ID to interact with"), _HELP("Target Entity")),
			InputPortConfig_Void( "StartInteraction", _HELP("Start the Interaction")),
			InputPortConfig_Void( "StopInteraction", _HELP("Stop this interaction")),
			InputPortConfig<string>("Limb01", _HELP("IK Handle as defined in the IK file or chrparams file.")),
			InputPortConfig<string>("Limb02", _HELP("IK Handle as defined in the IK file or chrparams file.")),
			InputPortConfig<int> ( "ObjectType", 0, _HELP("Object Type"), 0, _UICONFIG("enum_int:Default=0,TwoHandedWheel=1,OneHandedWheel=2,Handle=3,Lever=4,Button=5" )),	
			InputPortConfig<bool>( "AllowRegrip", true, _HELP("Allows/Disallow re-gripping when the position becomes unnatural" ), "Allow Regrip", 0),
			InputPortConfig<Vec3>( "OffsetPos", _HELP("OPTIONAL: Override of entity property. Leave blank for default value." )),
			InputPortConfig<Vec3>( "OffsetRot", _HELP("OPTIONAL: Override of entity property. Leave blank for default value." )),
			InputPortConfig_Null()
		};
		static const SOutputPortConfig out_ports[] = 
		{
			OutputPortConfig_Void("Success", _HELP("Activates when the Interaction could was started and the IK is fading in.")),
			OutputPortConfig_Void("Failed", _HELP("Activates when the Interaction could not be started.")),
			OutputPortConfig_Void("Finished", _HELP("Activates when the Interaction is finished and the IK is fading out. This will also trigger on abort or fail.")),
			OutputPortConfig_Null()
		};
		config.pInputPorts = in_ports;
		config.pOutputPorts = out_ports;
		config.nFlags |= EFLN_TARGET_ENTITY;
		config.sDescription = _HELP("Controls an interaction between a character and an IKObject.");
		config.SetCategory(EFLN_APPROVED);
	}

	virtual void GetMemoryUsage(ICrySizer * s) const
	{
		s->Add(*this);
	}


	void ProcessEvent( EFlowEvent event, SActivationInfo *pActInfo )
	{

		switch (event)
		{
		case eFE_Activate:

			if (!pActInfo->pEntity)
			{
				// activate failed output in case this was triggered by a startinteraction signal
				if (IsPortActive(pActInfo, EIP_StartInteraction))
					InteractionFailed(pActInfo);
				
				return;
			}

			if (IsPortActive(pActInfo, EIP_StartInteraction))
			{
				StartInteraction(pActInfo);
			}
			else if (IsPortActive(pActInfo, EIP_StopInteraction))
			{
				// Stop the Interaction
				StopInteraction(pActInfo);
			}
			break;

		case eFE_Update:

			// Check if still active
			CIKInteractionManager* pInteractionManager = CCryAction::GetCryAction()->GetIKInteractionManager();
			if (!pInteractionManager || !pInteractionManager->IsValidInteraction(m_interactionID))
			{
				// Activate the finished output if done and reset everything
				InteractionFinished( pActInfo );
				return;
			}

			break;
		}

	}

	void InteractionFailed( SActivationInfo *pActInfo )
	{
		InteractionFinished(pActInfo);
		ActivateOutput(pActInfo, EOP_Failed, true);
	}

	void InteractionFinished( SActivationInfo *pActInfo )
	{
		// stop the every frame updatsece
		pActInfo->pGraph->SetRegularlyUpdated( pActInfo->myID, false );
		ActivateOutput(pActInfo, EOP_Finished, true);
		m_interactionID = -1;
	}


	void StopInteraction( SActivationInfo * pActInfo )
	{
		CIKInteractionManager* pInteractionManager = CCryAction::GetCryAction()->GetIKInteractionManager();
		if (pInteractionManager)
			pInteractionManager->StopIKInteraction(m_interactionID);
	
		InteractionFinished(pActInfo);
	}

	void StartInteraction( SActivationInfo * pActInfo )
	{
		// Get IK Parameters
		EntityId	characterEntID = pActInfo->pEntity->GetId();

		// Get all the data from the entity
		EntityId targetEntityID = GetPortEntityId(pActInfo, EIP_TargetEntity);
		IEntity* targetEntity = gEnv->pEntitySystem->GetEntity(targetEntityID);
		if (!targetEntity)
		{
			InteractionFailed(pActInfo);
			return;
		}

		// Get two limbs, AllowRegrip and ObjectType
		bool allowRegrip = GetPortBool( pActInfo, EIP_AllowRegrip );
		EIK_Object_Types objectType = static_cast<EIK_Object_Types>(GetPortInt( pActInfo, EIP_ObjectType ));
		std::vector<uint64> ikLimbs;
		string limbName = GetPortString( pActInfo, EIP_Limb1 );
		if (strcmp(limbName, "") != 0)
			ikLimbs.push_back(*((uint64*)limbName.c_str()));
		limbName = GetPortString( pActInfo, EIP_Limb2 );
		if (strcmp(limbName, "") != 0)
			ikLimbs.push_back(*((uint64*)limbName.c_str()));

		// Create params struct with that information
		SIKInteractionStartParameters interactionParams(characterEntID, targetEntityID, 0, objectType, ikLimbs);
		if (!allowRegrip)	
			interactionParams.m_flags |= EIAC_NOREGRIP;

		// Read additional information from entity script
		CScriptBind_IKInteractions::ReadAditionalPropertiesFromEntity( targetEntity, &interactionParams );

		// override if necessary with values for offset
		Vec3 offsetPos = GetPortVec3(pActInfo, EIP_AdditionalOffsetPos);
		Vec3 offsetRot = GetPortVec3(pActInfo, EIP_AdditionalOffsetPos);
		if (!offsetPos.IsZero())
			interactionParams.m_additionalOffsetPos = offsetPos;
		if (!offsetRot.IsZero())
			interactionParams.m_additionalOffsetRot.SetRotationXYZ(Ang3(offsetRot));

		// Start interaction and store the id
		CIKInteractionManager* pInteractionManager = CCryAction::GetCryAction()->GetIKInteractionManager();
		if (!pInteractionManager)
		{
			//CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_ERROR, "IK Interaction Manager doesn't exist. Failing Interaction.", targetEntityID);
			InteractionFailed(pActInfo);
			return;
		}
		m_interactionID = pInteractionManager->StartNewIKInteraction(interactionParams);

		// Set to be regularly updated (so that the interaction can be checked (id still active))
		pActInfo->pGraph->SetRegularlyUpdated( pActInfo->myID, true );

		// Activate Output that it was successful
		ActivateOutput(pActInfo, EOP_Success, true);
	}

};

//////////////////////////////////////////////////////////////////////////

class CFlowNode_StopInteraction : public CFlowBaseNode<eNCT_Singleton>
{
	enum INPUTS
	{
		EIP_TargetEntity = 0,
		EIP_StopInteraction,
		EIP_BlendoutTime,
	};

	enum OUTPUTS
	{
		EOP_Finished,
	};

public:
	CFlowNode_StopInteraction( SActivationInfo * pActInfo ) { }

	void GetConfiguration( SFlowNodeConfig& config )
	{
		static const SInputPortConfig in_ports[] = 
		{
			InputPortConfig<EntityId>("TargetEntity", _HELP("Entity ID of object the character is interacting with"), _HELP("Target Entity")),
			InputPortConfig_Void( "StopInteraction", _HELP("Stop all ongoing interactions between this character and target object")),
			InputPortConfig<float>( "BlendoutTime", 0.7f, _HELP("Time to fade out the IK (in seconds). Set to 0.0 for immediate stopping.")),
			InputPortConfig_Null()
		};
		static const SOutputPortConfig out_ports[] = 
		{
			OutputPortConfig_Void("Finished", _HELP("This will trigger once the interaction was asked to stop (the node will not wait until the IK is faded out).")),
			OutputPortConfig_Null()
		};
		config.pInputPorts = in_ports;
		config.pOutputPorts = out_ports;
		config.nFlags |= EFLN_TARGET_ENTITY;
		config.sDescription = _HELP("Stops the interaction(s) between a character and an IKObject.");
		config.SetCategory(EFLN_APPROVED);
	}

	virtual void GetMemoryUsage(ICrySizer * s) const
	{
		s->Add(*this);
	}


	void ProcessEvent( EFlowEvent event, SActivationInfo *pActInfo )
	{

		switch (event)
		{
		case eFE_Activate:

			if (IsPortActive(pActInfo, EIP_StopInteraction))
			{
				if (!pActInfo->pEntity)
					return TriggerFinished(pActInfo);

				// retrieve entity id of character and object pair on which to stop the/any interactions
				EntityId characterEntID = pActInfo->pEntity->GetId();
				EntityId targetEntityID = GetPortEntityId(pActInfo, EIP_TargetEntity);

				// Get the blending out time
				float blendoutTime = GetPortFloat(pActInfo, EIP_BlendoutTime);
				if (blendoutTime < 0.0f)
					blendoutTime = 0.0f;

				// stop the interaction
				CIKInteractionManager* pInteractionManager = CCryAction::GetCryAction()->GetIKInteractionManager();
				if (!pInteractionManager)
					return TriggerFinished(pActInfo);
				pInteractionManager->StopIKInteraction(characterEntID, targetEntityID, blendoutTime);

				// finally, trigger the output so that the FG can continue
				TriggerFinished(pActInfo);
			}
			break;
		}
	}




	void TriggerFinished( SActivationInfo * pActInfo )
	{
		ActivateOutput(pActInfo, EOP_Finished, true);
	}

};

//////////////////////////////////////////////////////////////////////////

class CFlowNode_StopAllInteractions : public CFlowBaseNode<eNCT_Singleton>
{
	enum INPUTS
	{
		EIP_Trigger,
	};

public:
	CFlowNode_StopAllInteractions( SActivationInfo * pActInfo ) { }

	void GetConfiguration( SFlowNodeConfig& config )
	{
		static const SInputPortConfig in_ports[] = 
		{
			InputPortConfig_Void( "Trigger", _HELP("Stops all ongoing interactions")),
			InputPortConfig_Null()
		};
		static const SOutputPortConfig out_ports[] = 
		{
			OutputPortConfig_Null()
		};
		config.pInputPorts = in_ports;
		config.pOutputPorts = out_ports;
		//config.nFlags |= EFLN_TARGET_ENTITY;
		config.sDescription = _HELP("Stops all interactions immediately.");
		config.SetCategory(EFLN_APPROVED);
	}

	virtual void GetMemoryUsage(ICrySizer * s) const
	{
		s->Add(*this);
	}


	void ProcessEvent( EFlowEvent event, SActivationInfo *pActInfo )
	{

		switch (event)
		{
		case eFE_Activate:

			if (IsPortActive(pActInfo, EIP_Trigger))
			{
				// stop the interaction
				CIKInteractionManager* pInteractionManager = CCryAction::GetCryAction()->GetIKInteractionManager();
				if (!pInteractionManager)
					return;
				pInteractionManager->StopAllIKInteractions();
			}
			break;
		}
	}

};

//////////////////////////////////////////////////////////////////////////

class CFlowNode_EnableInteractions : public CFlowBaseNode<eNCT_Singleton>
{
	enum INPUTS
	{
		EIP_Enable,
		EIP_Disable
	};

public:
	CFlowNode_EnableInteractions( SActivationInfo * pActInfo ) { }

	void GetConfiguration( SFlowNodeConfig& config )
	{
		static const SInputPortConfig in_ports[] = 
		{
			InputPortConfig_Void( "Enable", _HELP("Enable the interaction system.")),
			InputPortConfig_Void( "Disable", _HELP("Disable the interaction system.")),
			InputPortConfig_Null()
		};
		static const SOutputPortConfig out_ports[] = 
		{
			OutputPortConfig_Null()
		};
		config.pInputPorts = in_ports;
		config.pOutputPorts = out_ports;
		//config.nFlags |= EFLN_TARGET_ENTITY;
		config.sDescription = _HELP("Globally enable/disable the Interaction system.\nRunning interactions will be stopped when the system is disabled.");
		config.SetCategory(EFLN_APPROVED);
	}

	virtual void GetMemoryUsage(ICrySizer * s) const
	{
		s->Add(*this);
	}


	void ProcessEvent( EFlowEvent event, SActivationInfo *pActInfo )
	{

		switch (event)
		{
		case eFE_Activate:
			{
				CIKInteractionManager* pInteractionManager = CCryAction::GetCryAction()->GetIKInteractionManager();
				if (!pInteractionManager)
					return;

				if (IsPortActive(pActInfo, EIP_Enable))
				{
					pInteractionManager->EnableSystem(true);
				}
				if (IsPortActive(pActInfo, EIP_Disable))
				{
					pInteractionManager->EnableSystem(false);
				}
			}
			break;
		}
	}

};

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

REGISTER_FLOW_NODE("Animations:IKInteraction:StartInteraction",	CFlowNode_StartInteraction);
REGISTER_FLOW_NODE("Animations:IKInteraction:StopInteraction", CFlowNode_StopInteraction);
REGISTER_FLOW_NODE("Animations:IKInteraction:StopAllInteractions", CFlowNode_StopAllInteractions);
REGISTER_FLOW_NODE("Animations:IKInteraction:EnableInteractions", CFlowNode_EnableInteractions);
